#
#  Copyright 2022 The Open Islands Authors. All Rights Reserved.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#
import time
from base64 import b64encode
from hmac import HMAC
from urllib.parse import quote, urlencode
from uuid import uuid1

import requests

from pyoi.runtime.types import CommonResponse
from pyoi.runtime_env import RuntimeENV
from pyoi.util.base_utils import json_dumps
from pyoi.util.log_utils import getLogger

LOGGER = getLogger()


def local_request(method, endpoint, json_body, overall_timeout=None, try_times=3) -> CommonResponse:
    overall_timeout = RuntimeENV.LOCAL_API_REQUEST_TIMEOUT if overall_timeout is None else overall_timeout
    return http_request(method=method, host=RuntimeENV.WORKER_SERVICE_HOST,
                        port=RuntimeENV.WORKER_SERVICE_HTTP_PORT, endpoint=endpoint, json_body=json_body,
                        overall_timeout=overall_timeout, try_times=try_times)


def http_request(method, host, port, endpoint, json_body, overall_timeout=None, try_times=3) -> CommonResponse:
    exception = None

    for t in range(try_times):
        try:
            url = "http://{}:{}{}".format(host, port, endpoint)
            headers = RuntimeENV.HEADERS.copy()
            response = request(method=method, url=url, data=json_dumps(json_body), headers=headers,
                               timeout=(overall_timeout // 1000))
            if response.status_code in {200, 201}:
                return CommonResponse.from_dict(response.json())
            else:
                raise Exception(f"send http request failed, request url: {url}, response code: {response.status_code}")
        except Exception as e:
            exception = e
            time.sleep(2 * (t + 1))
    else:
        raise exception


def request(**kwargs):
    sess = requests.Session()
    stream = kwargs.pop('stream', sess.stream)
    timeout = kwargs.pop('timeout', 60)
    prepped = requests.Request(**kwargs).prepare()

    if RuntimeENV.HTTP_APP_KEY and RuntimeENV.HTTP_SECRET_KEY:
        timestamp = str(round(time.time() * 1000))
        nonce = str(uuid1())
        signature = b64encode(HMAC(RuntimeENV.HTTP_SECRET_KEY.encode('ascii'), b'\n'.join([
            timestamp.encode('ascii'),
            nonce.encode('ascii'),
            RuntimeENV.HTTP_APP_KEY.encode('ascii'),
            prepped.path_url.encode('ascii'),
            prepped.body if kwargs.get('json') else b'',
            urlencode(sorted(kwargs['data'].items()), quote_via=quote, safe='-._~').encode('ascii')
            if kwargs.get('data') and isinstance(kwargs['data'], dict) else b'',
        ]), 'sha1').digest()).decode('ascii')

        prepped.headers.update({
            'TIMESTAMP': timestamp,
            'NONCE': nonce,
            'APP_KEY': RuntimeENV.HTTP_APP_KEY,
            'SIGNATURE': signature,
        })

    return sess.send(prepped, stream=stream, timeout=timeout)
